package com.hangwei.aicabinet.manager;

import android.annotation.SuppressLint;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;

import com.hangwei.aicabinet.App;
import com.hangwei.aicabinet.model.entry.AdminLoopComplete;
import com.hangwei.aicabinet.util.ByteUtil;
import com.hangwei.aicabinet.util.CRCUtil;
import com.hangwei.aicabinet.util.Constant;
import com.hangwei.aicabinet.util.InstructionsUtil;
import com.hangwei.aicabinet.util.LogUtil;
import com.hangwei.aicabinet.util.SPKey;
import com.hangwei.aicabinet.util.SPUtil;
import com.hangwei.aicabinet.util.UpLoadUtil;

import org.greenrobot.eventbus.EventBus;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

import android_serialport_api.SerialPort;

/**
 * Created by 张龙臣 on 2018/9/14.
 * <p>
 * 使用的时候 用SerialPortProxy 代理类进行调用
 * <p>
 * 使用工具类  ByteUtil  主要使用16进制字符串和byte数组相互转换
 * CRCUtil   指令校验和校验码生成
 * InstructionsUtil 指令的生成 从指令中提取数据 少数指令校验
 * Constant 常量
 * 串口调用的整套逻辑
 * 1.  一发一收：一个完整的发和收完成才会去进行下一个指令的操作，
 * 如果在规定时间里（轮询2000ms，其余500ms）没有收到返回指令视为超时，将会重发指令
 * 如果连续三次发送，接收指令时超时视为链接故障，抛出故障等待维修
 * 2.  接收到的指令校验：串口返回的一个指令有可能被分两次读到，把每次读到的数据进行CRC
 * 校验校验通过，视为完整数据可以执行，否则视为不完整数据存入到内存缓存中和下一次收到的数据
 * 进行拼接然后校验（在本代码逻辑中如果在上诉的规定时间里没有收到完整数据，指令也将会重发）
 * <p>
 * 测试读写在线会连续执行设置功率、获取可用天线
 * 设置天线会连续执行轮询读取标签、从缓存获取数据
 */

public class SerialPortManager {
    private static final int baudrate = 115200;//波特率（可自行设定）
    private static final String device = "/dev/ttyS3";

    private static Thread mReceiveThread;
    private static Thread mSendThread;

    private static SerialPort mSerialPort;
    private static InputStream mInputStream;
    private static OutputStream mOutputStream;

    static HashMap<String, String> allTabFromRead;
    private static ArrayList<String> availableAirLine;

    private static String order = "";
    private static String tempSetAntenna = "";

    private static int overTimeTimes = 0;
    //    private static String Opcode = "";
//    private static String STATUS = "";

    public static final String NoResponseError = "三次无响应";
    private static boolean allAirLine = false;

//    private static Handler countDownHandle = new Handler();

    public interface ReturnHandleCallBack {
        void error(int what, String msg);
    }

    private static ReturnHandleCallBack mReturnHandleCallBack;

    private static long totalTabCount = 0;
    @SuppressLint("HandlerLeak")
    private static Handler mReceiveHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            String back = (String) msg.obj;
            String status = "";
            try {
                status = back.substring(6, 10);
            } catch (IndexOutOfBoundsException e) {
                e.printStackTrace();
                Log.e("mReceiveHandler", back);
                return;
            }
            switch (msg.what) {
                case 1://读写器在线检测
                    if ("0000".equals(status))
//                        setPower();
                        getAntenna();
                    else mReturnHandleCallBack.error(1, back);
                    break;
//                case 2://设置读功率
//                    if ("0000".equals(status)) getAntenna();
//                    else mReturnHandleCallBack.error(2, back);
//                    break;
                case 3://获取天线连接状态
                    if ("0000".equals(status)) {
                        //需要解析出可用天线，对应去设置（设置了不存在的天线在读卡的时候会出现错误）
                        if (back.substring(12, back.length() - 4).length() % 4 == 0) {
                            availableAirLine.clear();
                            availableAirLine.addAll(InstructionsUtil.getAvailableAntenna(back));
                        } else {
                            Log.e("mReceiveHandler", "返回指令状态正常，但格式错误，重新发送指令");
                            getAntenna();
                        }
                    } else mReturnHandleCallBack.error(3, back);
                    break;
                case 4://设置工作天线
                    if ("0000".equals(status)) readCard();
                    else mReturnHandleCallBack.error(4, back);
                    break;
                case 5://轮询读卡
                    if ("0000".equals(status)) {
                        totalTabCount = ByteUtil.hexStr2decimal(back.substring(12, 16));
                        fromCache();
                    } else mReturnHandleCallBack.error(5, back);
                    break;
                case 6://从读写器缓存中读取标签
                    if ("0000".equals(status)) {
                        HashMap<String, String> tabs = InstructionsUtil.getTabFromResponse(back);

                        if (tabs != null && tabs.size() > 0) {
                            allTabFromRead.putAll(tabs);
                            Set<String> keySet = allTabFromRead.keySet();
                            Log.d("keySet", keySet.toString());
                            Log.i("keySet", keySet.size() + "");
                            if (totalTabCount > keySet.size())
                                fromCache();
                            else {
                                if (App.isAdmin) {
                                    EventBus.getDefault().post(new AdminLoopComplete());
                                } else {
                                    //标签读完了
                                    UpLoadUtil.updateLockStatus();
                                }
                                totalTabCount = 0;
                            }
                        } else {
                            //没有标签
                            if (App.isAdmin) {
                                EventBus.getDefault().post(new AdminLoopComplete());
                            } else
                                UpLoadUtil.updateLockStatus();
                            totalTabCount = 0;//是否需要清空？
                        }
                    } else mReturnHandleCallBack.error(6, back);
                    break;
                case 7:
                    if ("0000".equals(status)){
                        if (back.substring(14,16).equals("01")){
                            //是s1场景不需要设置
                            Log.d("mReceiveHandler","是s1场景无需重设");
                            testOnLine();
                        }else {
                            Log.i("mReceiveHandler","不是是s1场景重新设置");
                            setType();
                        }
                    }else mReturnHandleCallBack.error(7, back);
                    break;
                case 8:
                    if ("0000".equals(status)){
                        Log.i("mReceiveHandler","设置s1场景成功--");
                        testOnLine();
                    }else mReturnHandleCallBack.error(8, back);
                    break;
                default:
                    break;
            }
        }
    };

    /**
     * 打开串口
     */
    static boolean openDevice(@NonNull ReturnHandleCallBack callBack) {
        try {
            mSerialPort = new SerialPort(new File(device), baudrate, 0);
            mInputStream = mSerialPort.getInputStream();
            mOutputStream = mSerialPort.getOutputStream();

            if (allTabFromRead == null)
                allTabFromRead = new HashMap<>();
            else allTabFromRead.clear();
            if (availableAirLine == null)
                availableAirLine = new ArrayList<>();
            else availableAirLine.clear();

            mReturnHandleCallBack = callBack;
            openReceiveThread();
            openSendThread();
        } catch (IOException | SecurityException e) {
            LogUtil.e("打开串口失败");
//            ToastUtil.showCenterToast("打开串口失败");
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 读写器在线检测
     */
    static void testOnLine() {
        //清空操作码
        SPUtil.setString(SPKey.Opcode, "");
//        SPUtil.setString(SPKey.STATUS, "");
        stringBuilder = new StringBuilder();
        order = Constant.Check_Available;
        mReceiveHandler.postDelayed(testCallBack, 500);
    }

    static void getType() {
        SPUtil.setString(SPKey.Opcode, "");
        order = Constant.Get_Type;
        mReceiveHandler.postDelayed(getTypeCallBack, 500);
    }

    static void setType() {
        SPUtil.setString(SPKey.Opcode, "");
        order = Constant.Get_Type;
        mReceiveHandler.postDelayed(setTypeCallBack, 500);
    }

//    static void setPower() {
//        SPUtil.setString(SPKey.Opcode, "");
////        SPUtil.setString(SPKey.STATUS, "");
//        stringBuilder = new StringBuilder();
//        order = Constant.Set_Power;
//        mReceiveHandler.postDelayed(setPowerCallBack, 500);
//    }

    static void getAntenna() {
        SPUtil.setString(SPKey.Opcode, "");
//        SPUtil.setString(SPKey.STATUS, "");
        stringBuilder = new StringBuilder();
        order = Constant.Get_Antenna;
        mReceiveHandler.postDelayed(getAntennaCallBack, 500);
    }

    static void setAntenna(String antenna) {
        allAirLine = false;
        SPUtil.setString(SPKey.Opcode, "");
//        SPUtil.setString(SPKey.STATUS, "");
        stringBuilder = new StringBuilder();
        order = antenna;
        Log.i("设置天线", antenna + "---------------");
        tempSetAntenna = antenna;
        mReceiveHandler.postDelayed(setAntennaCallBack, 500);
    }

    static void setAntennaAll() {
        allAirLine = true;
        SPUtil.setString(SPKey.Opcode, "");
        stringBuilder = new StringBuilder();
        order = Constant.Set_All_Antenna;
        Log.i("设置天线", "-------aa--------");
        tempSetAntenna = Constant.Set_All_Antenna;
        mReceiveHandler.postDelayed(setAntennaCallBack, 2000);
    }

    static void readCard() {
        if (allTabFromRead != null)
            allTabFromRead.clear();
        else allTabFromRead = new HashMap<>();
        SPUtil.setString(SPKey.Opcode, "");
        stringBuilder = new StringBuilder();
//        SPUtil.setString(SPKey.STATUS, "");
        order = Constant.Read_Card;
        if (allAirLine) mReceiveHandler.postDelayed(readCardCallBack, 15000);
        else mReceiveHandler.postDelayed(readCardCallBack, 2000);
    }

    static void fromCache() {
        SPUtil.setString(SPKey.Opcode, "");
//        SPUtil.setString(SPKey.STATUS, "");
//        stringBuilder = new StringBuilder();
        order = Constant.From_Cache;
        mReceiveHandler.postDelayed(fromCacheCallBack, 3000);
    }

    private static StringBuilder stringBuilder;

    static {
        stringBuilder = new StringBuilder();

    }

    private static void openReceiveThread() {
        mReceiveThread = new Thread() {
            @Override
            public void run() {
                try {
                    while (true) {
                        mInputStream = mSerialPort.getInputStream();
                        if (mInputStream == null) {
                            Log.e("openReceiveThread", "mInputStream == null");
                            break;
                        }
                        int available = mInputStream.available();
                        if (available > 0) {
                            byte[] buffer = new byte[available];
                            int read = mInputStream.read(buffer);
                            String hexStr = ByteUtil.bytes2HexStr(buffer);
                            overTimeTimes = 0;
                            Log.i("ReceiveThread", hexStr);
                            if (!TextUtils.isEmpty(stringBuilder.toString())) {
                                //有不完整的数据把这次的数据拼接进去
                                stringBuilder.append(hexStr);
                                hexStr = stringBuilder.toString();
                            }
                            //如果超过等待时间还没有完整（CRC校验通过）的数据将会重发指令
                            if (hexStr.length() >= 14) {
                                String crc = hexStr.substring(hexStr.length() - 4);
                                String s = hexStr.substring(2, hexStr.length() - 4);
                                byte[] bytes = CRCUtil.crcTable(ByteUtil.hexStr2bytes(s), 0, s.length() / 2);
                                if (crc.equals(ByteUtil.bytes2HexStr(bytes))) {
                                    //校验通过，是完整的数据
                                    stringBuilder = new StringBuilder();//清除sb
                                    Message obtain = Message.obtain();
                                    obtain.what = 10;//设置一个非正常值
                                    obtain.obj = hexStr;
                                    String opCode = hexStr.substring(4, 6);
                                    String temp = "";
                                    switch (opCode) {
                                        case Constant.Opcode.testOnline:
                                            mReceiveHandler.removeCallbacks(testCallBack);
                                            temp = Constant.Opcode.testOnline;
                                            obtain.what = 1;
                                            break;
//                                        case Constant.Opcode.setPower:
//                                            mReceiveHandler.removeCallbacks(setPowerCallBack);
//                                            temp = Constant.Opcode.setPower;
//                                            obtain.what = 2;
//                                            break;
                                        case Constant.Opcode.getType:
                                            mReceiveHandler.removeCallbacks(getTypeCallBack);
                                            temp = Constant.Opcode.getType;
                                            obtain.what = 7;
                                            break;
                                        case Constant.Opcode.setType:
                                            mReceiveHandler.removeCallbacks(setTypeCallBack);
                                            temp = Constant.Opcode.setType;
                                            obtain.what = 8;
                                            break;
                                        case Constant.Opcode.getAntenna:
                                            mReceiveHandler.removeCallbacks(getAntennaCallBack);
                                            temp = Constant.Opcode.getAntenna;
                                            obtain.what = 3;
                                            break;
                                        case Constant.Opcode.setAntenna:
                                            tempSetAntenna = "";
                                            mReceiveHandler.removeCallbacks(setAntennaCallBack);
                                            temp = Constant.Opcode.setAntenna;
                                            obtain.what = 4;
                                            break;
                                        case Constant.Opcode.readCard:
                                            mReceiveHandler.removeCallbacks(readCardCallBack);
                                            temp = Constant.Opcode.readCard;
                                            obtain.what = 5;
                                            break;
                                        case Constant.Opcode.fromCache:
                                            mReceiveHandler.removeCallbacks(fromCacheCallBack);
                                            temp = Constant.Opcode.fromCache;
                                            obtain.what = 6;
                                            break;
                                    }
                                    if (!TextUtils.isEmpty(temp))
                                        SPUtil.setString(SPKey.Opcode, temp);
//                                    String status = hexStr.substring(6, 10);
//                                    SPUtil.setString(SPKey.STATUS, status);
                                    mReceiveHandler.sendMessage(obtain);
                                    Thread.sleep(50);
                                } else {
                                    //校验未通过视为不完整数据
                                    stringBuilder.append(hexStr);
                                    Log.e("校验未通过视为不完整数据", hexStr);
                                }
                            } else {
                                //返回的不到14位数据肯定不完整（头2长度2操作码2状态码4校验码4）
//                                if (TextUtils.isEmpty(stringBuilder.toString()))
                                stringBuilder.append(hexStr);
                                Log.e("不到14位数", hexStr);
                            }
                            Thread.sleep(50);
                        } else {
                            Thread.sleep(50);
//                            Log.d("ReceiveThread无数据", "-----");
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.e("IOException", "-----ReceiveThread------");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        mReceiveThread.start();
    }

    private static void openSendThread() {
        mSendThread = new Thread() {
            @Override
            public void run() {
                try {
                    while (true) {
                        mOutputStream = mSerialPort.getOutputStream();
                        if (mOutputStream == null) {
                            Log.e("openSendThread", "mOutputStream == null");
                            break;
                        }
                        if (TextUtils.isEmpty(order)) {
                            Thread.sleep(50);
                            continue;
                        }
                        byte[] bytes = ByteUtil.hexStr2bytes(order);
                        mOutputStream.write(bytes);
                        Log.i("发送成功", order);
                        order = "";
                        Thread.sleep(50);
                    }
                } catch (IOException e) {
                    Log.e("IOException", "-----SendThread------");
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        mSendThread.start();
    }

    static void closeSerialPort() {
        if (mSerialPort != null) {
            mSerialPort.close();
        }
        if (mInputStream != null) {
            try {
                mInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (mOutputStream != null) {
            try {
                mOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private static boolean checkOverTime(String op) {
        String string = SPUtil.getString(SPKey.Opcode);
        return !op.equals(string);
    }

    private static Runnable testCallBack = new Runnable() {
        @Override
        public void run() {
            Log.i("testCallBack", "testCallBack");
            boolean overTime = checkOverTime("03");
            if (overTime) {
                if (overTimeTimes < 2) {
                    overTimeTimes++;
                    Log.i("testCallBack", "重新发送第" + overTimeTimes + "次");
                    testOnLine();
                } else {
                    mReturnHandleCallBack.error(1, NoResponseError);
                    overTimeTimes = 0;
                }
            } else {
                overTimeTimes = 0;
            }
        }
    };
    private static Runnable getTypeCallBack = new Runnable() {
        @Override
        public void run() {
            Log.i("testCallBack", "testCallBack");
            boolean overTime = checkOverTime("6B");
            if (overTime) {
                if (overTimeTimes < 2) {
                    overTimeTimes++;
                    Log.i("getTypeCallBack", "重新发送第" + overTimeTimes + "次");
                    testOnLine();
                } else {
                    mReturnHandleCallBack.error(1, NoResponseError);
                    overTimeTimes = 0;
                }
            } else {
                overTimeTimes = 0;
            }
        }
    };
    private static Runnable setTypeCallBack = new Runnable() {
        @Override
        public void run() {
            Log.i("testCallBack", "testCallBack");
            boolean overTime = checkOverTime("9B");
            if (overTime) {
                if (overTimeTimes < 2) {
                    overTimeTimes++;
                    Log.i("setTypeCallBack", "重新发送第" + overTimeTimes + "次");
                    testOnLine();
                } else {
                    mReturnHandleCallBack.error(1, NoResponseError);
                    overTimeTimes = 0;
                }
            } else {
                overTimeTimes = 0;
            }
        }
    };

//    private static Runnable setPowerCallBack = new Runnable() {
//        @Override
//        public void run() {
//            boolean overTime = checkOverTime("92");
//            if (overTime) {
//                if (overTimeTimes < 2) {
//                    overTimeTimes++;
//                    Log.i("setPowerCallBack", "重新发送第" + overTimeTimes + "次");
//                    setPower();
//                } else {
//                    mReturnHandleCallBack.error(2, NoResponseError);
//                }
//            } else {
//                overTimeTimes = 0;
//            }
//        }
//    };

    private static Runnable getAntennaCallBack = new Runnable() {
        @Override
        public void run() {
            boolean overTime = checkOverTime("61");
            if (overTime) {
                if (overTimeTimes < 2) {
                    overTimeTimes++;
                    Log.i("getAntennaCallBack", "重新发送第" + overTimeTimes + "次");
                    getAntenna();
                } else {
                    mReturnHandleCallBack.error(3, NoResponseError);
                    overTimeTimes = 0;
                }
            } else {
                overTimeTimes = 0;
            }
        }
    };

    private static Runnable setAntennaCallBack = new Runnable() {
        @Override
        public void run() {
            boolean overTime = checkOverTime("91");
            if (overTime) {
                if (overTimeTimes < 2) {
                    overTimeTimes++;
                    Log.i("setAntennaCallBack", "重新发送第" + overTimeTimes + "次");
                    if (allAirLine)
                        setAntennaAll();
                    else
                        setAntenna(tempSetAntenna);
                } else {
                    tempSetAntenna = "";
                    mReturnHandleCallBack.error(4, NoResponseError);
                    overTimeTimes = 0;
                }
            } else {
                overTimeTimes = 0;
            }
        }
    };

    private static Runnable readCardCallBack = new Runnable() {
        @Override
        public void run() {
            boolean overTime = checkOverTime("91");
            if (overTime) {
                if (overTimeTimes < 2) {
                    overTimeTimes++;
                    Log.i("readCardCallBack", "重新发送第" + overTimeTimes + "次");
                    readCard();
                } else {
                    mReturnHandleCallBack.error(5, NoResponseError);
                    overTimeTimes = 0;
                }
            } else {
                overTimeTimes = 0;
            }
        }
    };


    private static Runnable fromCacheCallBack = new Runnable() {
        @Override
        public void run() {
            boolean overTime = checkOverTime("91");
            if (overTime) {
                if (overTimeTimes < 2) {
                    overTimeTimes++;
                    Log.i("fromCacheCallBack", "重新发送第" + overTimeTimes + "次");
                    stringBuilder = new StringBuilder();
                    fromCache();
                } else {
                    mReturnHandleCallBack.error(6, NoResponseError);
                    overTimeTimes = 0;
                }
            } else {
                overTimeTimes = 0;
            }
        }
    };
}
